
/**
\file Definition of nmn::math::quaternion.
\author Sean Tromans
*/

#ifndef	NMN_TEMPLATE_MATH_QUATERNION_HPP
#define	NMN_TEMPLATE_MATH_QUATERNION_HPP

#include <needs_more_ninja/template/math/vector/vector.hpp>

namespace nmn
{

	namespace math
	{

		/**
		Generic 3D quaternion.
		\author Sean Tromans
		*/
		template < typename TValueType >
		class quaternion	:	public	boost::additive1< typename quaternion< TValueType >,
										boost::multipliable1< typename quaternion< TValueType >,
										boost::multiplicative2< typename quaternion< TValueType >, TValueType,
										boost::equality_comparable1< typename quaternion< TValueType > > > > >
		{
			//
			//	Public Member Types
			//
			public:

				typedef	quaternion< TValueType >			my_type;
				typedef nmn::math::vector< TValueType, 4 >	vector_type;
				typedef typename vector_type::size_type		size_type;
				enum { static_size = vector_type::static_size };

				typedef	typename vector_type::value_type			value_type;
				typedef	typename vector_type::reference_type		reference_type;
				typedef	typename vector_type::const_reference_type	const_reference_type;

				typedef typename vector_type::iterator					iterator;
				typedef typename vector_type::const_iterator			const_iterator;
				typedef typename vector_type::reverse_iterator			reverse_iterator;
				typedef typename vector_type::const_reverse_iterator	const_reverse_iterator;

			//
			//	Public Member Functions
			//
			public:

				/**
				Default constructor. Does nothing.
				\author Sean Tromans
				*/
				quaternion( );

				/**
				Copy constructr from another quaternion.
				@param rhs The quaternion to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				quaternion( quaternion< TOtherValueType > const & rhs );

				/**
				Construct a quaternion from the four components
				@param x The x component.
				@param y The y component.
				@param z The z component.
				@param w The w component.
				\author Sean Tromans
				*/
				template < typename T0, typename T1, typename T2, typename T3 >
				quaternion( T0 const & x, T1 const & y, T2 const & z, T3 const & w );

				/**
				Assign this quaternion to be equal to another.
				@param rhs The quaternion to copy.
				\author Sean Tromans
				*/
				template < typename TOtherValueType >
				my_type & operator = ( quaternion< TOtherValueType > const & rhs );

				/**
				The size of the quaternion.
				\author Sean Tromans
				*/
				size_type size( ) const;

				/**
				Component wise addition of another quaternion onto this.
				@param rhs The quaternion to be added.
				@return A reference to this after the addition.
				\author Sean Tromans
				*/
				my_type & operator += ( my_type const & rhs );

				/**
				Component wise subtraction of another quaternion onto this.
				@param rhs The quaternion to be subtracted.
				@return A reference to this after the subtraction.
				\author Sean Tromans
				*/
				my_type & operator -= ( my_type const & rhs );

				/**
				Component wise multiplication of another quaternion onto this.
				@param rhs The quaternion to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( my_type const & rhs );

				/**
				Component wise multiplication of a value type onto this.
				@param rhs The value type to be multiplied.
				@return A reference to this after the multiplication.
				\author Sean Tromans
				*/
				my_type & operator *= ( value_type const & rhs );

				/**
				Component wise division of a value type onto this.
				@param rhs The value type to be divided.
				@return A reference to this after the division.
				\author Sean Tromans
				*/
				my_type & operator /= ( value_type const & rhs );

				/**
				Component wise strict equality check against another quaternion.
				@param rhs The quaternion to compare against.
				@return True if each corresponding component in the two quaternion are exactly equal.
				\author Sean Tromans
				*/
				bool strict_equals( my_type const & rhs ) const;

				/**
				Gets a component from inside the quaternion.
				@param index The index of the component to return.
				@return a reference to the component at \c index.
				\author Sean Tromans
				*/
				reference_type operator[] ( size_type index );

				/**
				Gets a component from inside the quaternion.
				@param index The index of the component to return.
				@return a constant reference to the component at \c index.
				\author Sean Tromans
				*/
				const_reference_type operator[] ( size_type index ) const;

				/**
				Gets a component from inside the quaternion.
				@param index The index of the component to return.
				@return a reference to the component at \c index.
				\author Sean Tromans
				*/
				reference_type at( size_type index );

				/**
				Gets a component from inside the quaternion.
				@param index The index of the component to return.
				@return a constant reference to the component at \c index.
				\author Sean Tromans
				*/
				const_reference_type at( size_type index ) const;

				/**
				Returns an iterator to the first element in the quaternion.
				\author Sean Tromans
				*/
				iterator begin( );

				/**
				Returns a delimiting iterator to the quaternion.
				\author Sean Tromans
				*/
				iterator end( );

				/**
				Returns a constant iterator to the first element in the quaternion.
				\author Sean Tromans
				*/
				const_iterator begin( ) const;

				/**
				Returns a delimiting constant iterator to the quaternion.
				\author Sean Tromans
				*/
				const_iterator end( ) const;

				/**
				Returns a constant iterator to the first element in the quaternion.
				\author Sean Tromans
				*/
				const_iterator cbegin( ) const;

				/**
				Returns a delimiting constant iterator to the quaternion.
				\author Sean Tromans
				*/
				const_iterator cend( ) const;

				/**
				Returns a reverse iterator to the last element in the quaternion.
				\author Sean Tromans
				*/
				reverse_iterator rbegin( );

				/**
				Returns a delimiting reverse iterator to the quaternion.
				\author Sean Tromans
				*/
				reverse_iterator rend( );

				/**
				Returns a constant reverse iterator to the last element in the quaternion.
				\author Sean Tromans
				*/
				const_reverse_iterator rbegin( ) const;

				/**
				Returns a delimiting constant reverse iterator to the quaternion.
				\author Sean Tromans
				*/
				const_reverse_iterator rend( ) const;

				/**
				Swaps this quaternion with another.
				@param rhs The quaternion to swap with.
				\author Sean Tromans
				*/
				void swap( my_type & rhs );

				/**
				Retreives the raw data contained within the quaternion.
				\author Sean Tromans
				*/
				value_type const * data( ) const;

			//
			//	Private Member Types
			//
			private:

				vector_type m_vector;
				
				static_assert( boost::is_arithmetic< TValueType >::value, "TValueType must be a numeric value type." );
		};

		/**
		\relates nmn::math::quaternion
		Outputs a vector2 to an output stream.
		@param os The output ostream.
		@param vec The vector to output.
		@return A reference to the output stream.
		\author Sean Tromans
		*/
		template < typename Elem, typename Traits, typename TValueType >
		inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & os, quaternion< TValueType > const & q );

		/**
		\relates nmn::math::quaternion
		Component wise absolute equality check.
		@param lhs The first quaternion to compare.
		@param rhs The second quaternion to compare.
		@return true if the two quaternion are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType >
		bool operator == ( quaternion< TValueType > const & lhs, quaternion< TValueType > const & rhs );

	};

};

namespace std
{
	/**
	\relates nmn::math::quaternion
	Swaps the values of two quaternion.
	@param lhs The first quaternion.
	@param rhs The second quaternion.
	\author Sean Tromans
	*/
	template < typename TValueType >
	void swap( nmn::math::quaternion< TValueType > & lhs, nmn::math::quaternion< TValueType > & rhs );
}

#endif	//NMN_TEMPLATE_MATH_QUATERNION_HPP
